#ifndef GRAPHICSPROXY_H
#define GRAPHICSPROXY_H

#include "symbiandepend.h"

#ifdef __SYMBIAN32__
#include <e32std.h>
#include <e32base.h>
#include <gdi.h>
#include <CStack.h>
#include <fbs.h>
#else
#include "ximage.h"
#endif

#include "rect.h"

class MControlContext;

class CGraphicsProxy 
#ifdef __SYMBIAN32__
	: public CBase
#endif
{
public:
	~CGraphicsProxy();
	static CGraphicsProxy* NewL(const MControlContext& aParent);
	static CGraphicsProxy* NewLC(const MControlContext& aParent);

private:
	CGraphicsProxy(const MControlContext& aParent);
	void ConstructL();

public:
	inline void AttachGc(
#ifdef __SYMBIAN32__
		CBitmapContext& 
#else
		HDC
#endif
		aGc) const;

	inline void SetPenStyle(CGraphicsContext::TPenStyle aVal);
	inline void SetBrushStyle(CGraphicsContext::TBrushStyle aVal);

#ifdef WINCE
	inline void SetTextColor(const TRgb& dwColor);
#endif

	inline void SetPenColor(const TRgb& aColor);
	inline void SetBrushColor(const TRgb& aColor);
	inline void DrawRect(const TRect& aRect);
	inline void DrawRect(TInt x, TInt y, TInt w, TInt h);
	inline void DrawRoundRect(TInt x, TInt y, TInt w, TInt h, TInt a, TInt b);
	inline void FillRect(TInt x, TInt y, TInt w, TInt h);
	inline void FillRoundRect(TInt x, TInt y, TInt w, TInt h, TInt arcX, TInt arcY);
	TBool SetClipL(TInt aCx, TInt aCy, TInt aCw, TInt aCh);
	void RestoreClip();
	void Translate(TInt x, TInt y);
	inline void DrawLine(TInt x1, TInt y1, TInt x2, TInt y2);
	inline void SetFont(const CFont& aVal);
	inline void DiscardFont();

	inline void DrawImage(const TPoint &aPoint, const CFbsBitmap* aBitmap);

#ifdef __SYMBIAN32__
	inline void DrawImage(const TPoint& aPoint
		, const CFbsBitmap* aBitmap
		, const CFbsBitmap* aMaskBitmap);
#endif

	inline void BitBltMasked(const TPoint &aPoint
						, const CFbsBitmap* aBitmap
						, const TRect &aSourceRect
#ifdef __SYMBIAN32__
						, const CFbsBitmap* aMaskBitmap
						, TBool aInvertMask
#endif
						);

#ifdef __SYMBIAN32__
	inline void SetUnderlineStyle(TFontUnderline aVal);
#endif

	inline void DrawString(const TDesC& aText, const TPoint &aPosition);
	inline void DrawString(const TDesC& aText, TInt aX, TInt aY);
	inline void DrawSubstring(const TDesC& aText, TInt aOffset, TInt aLen, TInt aX, TInt aY);
	inline void DrawEllipse(const TRect& aRect);
	inline TInt TranslateX() const;
	inline TInt TranslateY() const;
	inline TRect ClipRect() const;
	inline CFont* Font() const;

#ifdef WINCE
private:
	inline void ConfirmSelectPen();
	inline void ConfirmSelectBrush();
#endif

private:
	const MControlContext&			iParent;

public:
#ifdef __SYMBIAN32__
	mutable CBitmapContext* 	
#else
	mutable HDC
#endif
									iInnerGc;

private:
#ifdef __SYMBIAN32__
	CStack<TRectEx, ETrue>*		
#else
	std::vector<TRectEx*>*
#endif
									iStoredClipsets;
	TPoint 							iTransPoint;
	CFont*							iFont;

#ifdef WINCE
	//HDC								m_hTempGc;
	HPEN							m_hOldPen;
	HPEN							m_hCurrPen;
	HBRUSH							m_hOldBrush;
	HBRUSH							m_hCurrBrush;
	HFONT							m_hOldFont;
	CGraphicsContext::TPenStyle		m_PenStyle;
	CGraphicsContext::TBrushStyle	m_BrushStyle;
	COLORREF						m_dwPenColor;
	COLORREF						m_dwBrushColor;
#endif
};

inline void CGraphicsProxy::AttachGc(
#ifdef __SYMBIAN32__
	CBitmapContext& 
#else
	HDC
#endif
	aGc) const
{
#ifdef __SYMBIAN32__
	iInnerGc = &aGc;
#else
	iInnerGc = aGc;
#endif
}

inline void CGraphicsProxy::SetPenStyle(CGraphicsContext::TPenStyle aVal)
{
#ifdef __SYMBIAN32__
	if (iInnerGc != NULL)
		iInnerGc->SetPenStyle(aVal);
#else
	if (m_PenStyle != aVal)
	{
		m_PenStyle = aVal;
		ConfirmSelectPen();
	}
#endif
}

inline void CGraphicsProxy::SetBrushStyle(CGraphicsContext::TBrushStyle aVal)
{
#ifdef __SYMBIAN32__
	if (iInnerGc != NULL)
		iInnerGc->SetBrushStyle(aVal);
#else
	if (m_BrushStyle != aVal) 
	{
		m_BrushStyle = aVal;
		ConfirmSelectBrush();
	}
#endif
}

#ifdef WINCE
inline void CGraphicsProxy::SetTextColor(const TRgb& dwColor)
{
	if (iInnerGc != NULL)
		::SetTextColor(iInnerGc, dwColor);
}
#endif

inline void CGraphicsProxy::SetPenColor(const TRgb& aColor)
{
#ifdef __SYMBIAN32__
	if (iInnerGc != NULL)
		iInnerGc->SetPenColor(aColor);
#else
	if (m_dwPenColor != aColor)
	{
		m_dwPenColor = aColor;
		ConfirmSelectPen();
	}
#endif
}

inline void CGraphicsProxy::SetBrushColor(const TRgb& aColor)
{
#ifdef __SYMBIAN32__
	if (iInnerGc != NULL)
		iInnerGc->SetBrushColor(aColor);
#else
	if (m_dwBrushColor != aColor)
	{
		m_dwBrushColor = aColor;
		ConfirmSelectBrush();
	}
#endif
}

inline void CGraphicsProxy::DrawRect(const TRect& aRect)
{
	if (iInnerGc != NULL)
#ifdef __SYMBIAN32__
		iInnerGc->DrawRect(aRect);
#else
		::Rectangle(iInnerGc
					, aRect.iTl.iX + iTransPoint.iX
					, aRect.iTl.iY + iTransPoint.iY
					, aRect.iBr.iX + iTransPoint.iX
					, aRect.iBr.iY + iTransPoint.iY);
#endif
}

inline void CGraphicsProxy::DrawRect(TInt x, TInt y, TInt w, TInt h)
{
	//if (iInnerGc != NULL)
	//	iInnerGc->DrawRect(TRect(x, y, x + w, y + h));
	DrawRect(TRect(x, y, x + w, y + h));
}

inline void CGraphicsProxy::DrawRoundRect(TInt x, TInt y, TInt w, TInt h, TInt a, TInt b)
{
#ifdef __SYMBIAN32__
	if (iInnerGc != NULL)
		iInnerGc->DrawRoundRect(TRect(x, y, x + w, y + h), TSize(a, b));
#else
	SetPenStyle(CGraphicsContext::ESolidPen);
	SetBrushStyle(CGraphicsContext::ENullBrush);
	::RoundRect(iInnerGc
				, x + iTransPoint.iX
				, y + iTransPoint.iY
				, x + w + iTransPoint.iX
				, y + h + iTransPoint.iY
				, a
				, b);
#endif
}

inline void CGraphicsProxy::FillRect(TInt x, TInt y, TInt w, TInt h)
{
	if (iInnerGc != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->SetPenStyle(CGraphicsContext::ENullPen);
		iInnerGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
		iInnerGc->DrawRect(TRect(x, y, x + w, y + h));
#else
		RECT rc = {x + iTransPoint.iX
			, y + iTransPoint.iY
			, x + w + iTransPoint.iX
			, y + h + iTransPoint.iY};

		HBRUSH hBrush = ::CreateSolidBrush(m_dwBrushColor);

		::FillRect(iInnerGc
			, &rc
			, hBrush);

		::DeleteObject(hBrush);
#endif
	}
}

inline void CGraphicsProxy::FillRoundRect(TInt x, TInt y, TInt w, TInt h, TInt arcX, TInt arcY)
{
	if (iInnerGc != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->SetPenStyle(CGraphicsContext::ENullPen);
		iInnerGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
		iInnerGc->DrawRoundRect(TRect(x, y, x + w, y + h), TSize(arcX, arcY));
#else
		SetPenStyle(CGraphicsContext::ENullPen);
		SetBrushStyle(CGraphicsContext::ESolidBrush);
		::RoundRect(iInnerGc
			, x + iTransPoint.iX
			, y + iTransPoint.iY
			, x + w + iTransPoint.iX
			, y + h + iTransPoint.iY
			, arcX
			, arcY);
#endif
	}
}

inline void CGraphicsProxy::DrawLine(TInt x1, TInt y1, TInt x2, TInt y2)
{
	if (iInnerGc != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->DrawLine(TPoint(x1, y1), TPoint(x2, y2));
#else
		::MoveToEx(iInnerGc
			, x1 + iTransPoint.iX
			, y1 + iTransPoint.iY
			, NULL);

		::LineTo(iInnerGc
			, x2 + iTransPoint.iX
			, y2 + iTransPoint.iY);
#endif
	}
}

inline void CGraphicsProxy::SetFont(const CFont& aVal)
{
	if (iInnerGc != NULL)
	{
		iFont = (CFont*) &aVal;

#ifdef __SYMBIAN32__
		iInnerGc->UseFont(&aVal);
#else
		if (aVal.m_hFont != NULL)
		{
			if (m_hOldFont != NULL)
			{
				::SelectObject(iInnerGc, m_hOldFont);
				m_hOldFont = NULL;
			}

			m_hOldFont = (HFONT)::SelectObject(iInnerGc, aVal.m_hFont);
		}

#endif
	}
}

inline void CGraphicsProxy::DiscardFont()
{
	if (iInnerGc != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->DiscardFont();
#else
		if (m_hOldFont != NULL)
		{
			::SelectObject(iInnerGc, m_hOldFont);
			m_hOldFont = NULL;
		}
#endif

		iFont = NULL;
	}
}

inline void CGraphicsProxy::DrawImage(const TPoint& aPoint, const CFbsBitmap* aBitmap)
{
	if (iInnerGc != NULL && aBitmap != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->BitBlt(aPoint, aBitmap);
#else
		/*if (m_hTempGc == NULL)
			m_hTempGc = ::CreateCompatibleDC(iInnerGc);

		//SIZE bmpSize;
		//::GetBitmapDimensionEx(hBitmap, &bmpSize);
		HBITMAP hOldBmp = (HBITMAP)::SelectObject(m_hTempGc, *aBitmap);

		//::BitBlt(iInnerGc
		//		, point.x + iTransPoint.x
		//		, point.y + iTransPoint.y
		//		, nImgW
		//		, nImgH
		//		, m_hTempGc
		//		, 0
		//		, 0
		//		, SRCCOPY);

		BLENDFUNCTION bf = {AC_SRC_OVER, 0, 250, 0};
		::AlphaBlend(iInnerGc
			, aPoint.iX + iTransPoint.iX
			, aPoint.iY + iTransPoint.iY
			, aBitmap->SizeInPixels().iWidth
			, aBitmap->SizeInPixels().iHeight
			, m_hTempGc
			, 0
			, 0
			, aBitmap->SizeInPixels().iWidth
			, aBitmap->SizeInPixels().iHeight
			, bf);

		::SelectObject(m_hTempGc, hOldBmp);*/

		if (aBitmap->m_pRealImg != NULL)
		{
			RECT rtDisp = {aPoint.iX + iTransPoint.iX
						, aPoint.iY + iTransPoint.iY
						, aPoint.iX + iTransPoint.iX + aBitmap->m_szSize.iWidth
						, aPoint.iY + iTransPoint.iY + aBitmap->m_szSize.iHeight};

			aBitmap->m_pRealImg->Draw(iInnerGc, rtDisp);
		}
#endif
	}
}

#ifdef __SYMBIAN32__
inline void CGraphicsProxy::DrawImage(const TPoint& aPoint
									, const CFbsBitmap* aBitmap
									, const CFbsBitmap* aMaskBitmap)
{
	if (iInnerGc != NULL && aBitmap != NULL)
	{
		if (aMaskBitmap == NULL)
			iInnerGc->BitBlt(aPoint, aBitmap);
		else
			iInnerGc->BitBltMasked(aPoint
			, aBitmap
			, TRect(0, 0, aBitmap->SizeInPixels().iWidth, aBitmap->SizeInPixels().iHeight)
			, aMaskBitmap
			, EFalse);
	}
}
#endif

inline void CGraphicsProxy::BitBltMasked(const TPoint& aPoint
										, const CFbsBitmap* aBitmap
										, const TRect& aSourceRect
#ifdef __SYMBIAN32__
										, const CFbsBitmap* aMaskBitmap
										, TBool aInvertMask
#endif
										)
{
	if (iInnerGc != NULL && aBitmap != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->BitBltMasked(aPoint, aBitmap, aSourceRect, aMaskBitmap, aInvertMask);
#else
		if (aBitmap->m_pRealImg != NULL)
		{
			RECT rtDisp = 
			{
				aPoint.iX
				, aPoint.iY
				, aPoint.iX + aBitmap->m_szSize.iWidth
				, aPoint.iY + aBitmap->m_szSize.iHeight
			};

			RECT rtClip = 
			{
				aPoint.iX + aSourceRect.iTl.iX
				, aPoint.iY + aSourceRect.iTl.iY
				, aPoint.iX + aSourceRect.iBr.iX
				, aPoint.iY + aSourceRect.iBr.iY
			};

			aBitmap->m_pRealImg->Draw(iInnerGc, rtDisp, &rtClip);
		}
#endif
	}
}

#ifdef __SYMBIAN32__
inline void CGraphicsProxy::SetUnderlineStyle(TFontUnderline aVal)
{
	if (iInnerGc != NULL)
		iInnerGc->SetUnderlineStyle(aVal);
}
#endif

inline void CGraphicsProxy::DrawString(const TDesC& aText, const TPoint& aPosition)
{
	if (iInnerGc != NULL 
#ifdef __SYMBIAN32__
		&& iFont != NULL
#endif
		)
	{
#ifdef __SYMBIAN32__
		TPoint pos(aPosition);
		pos.iY += iFont->HeightInPixels();
		iInnerGc->DrawText(aText, pos);
#else
		::SetBkMode(iInnerGc, TRANSPARENT);

		RECT rect = {aPosition.iX + iTransPoint.iX
			, aPosition.iY + iTransPoint.iY
			, aPosition.iX + iTransPoint.iX
			, aPosition.iY + iTransPoint.iY};

		::DrawText(iInnerGc
			, aText.c_str()
			, aText.length()
			, &rect
			, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOCLIP | DT_NOPREFIX);

#endif
	}
}

inline void CGraphicsProxy::DrawString(const TDesC &aText, TInt aX, TInt aY)
{
	DrawString(aText, TPoint(aX, aY));
}

inline void CGraphicsProxy::DrawSubstring(const TDesC &aText, TInt aOffset, TInt aLen, TInt aX, TInt aY)
{
	DrawString(aText.STRMID(aOffset, aLen), aX, aY);
}

inline void CGraphicsProxy::DrawEllipse(const TRect& aRect)
{
	if (iInnerGc != NULL)
	{
#ifdef __SYMBIAN32__
		iInnerGc->DrawEllipse(aRect);
#else
		int nX1 = aRect.iTl.iX + iTransPoint.iX;
		int nY1 = aRect.iTl.iY + iTransPoint.iY;
		int nX2 = aRect.iBr.iX + iTransPoint.iX;
		int nY2 = aRect.iBr.iY + iTransPoint.iY;

		::Ellipse(iInnerGc, nX1, nY1, nX2, nY2);
#endif
	}
}

inline TInt CGraphicsProxy::TranslateX() const
{
	return iTransPoint.iX;
}

inline TInt CGraphicsProxy::TranslateY() const
{
	return iTransPoint.iY;
}

inline TRect CGraphicsProxy::ClipRect() const
{
#ifdef __SYMBIAN32__
	if (iStoredClipsets->Count() == 0)
		return TRect(0, 0, 0, 0);

	return *iStoredClipsets->Head();
#else
	if (iStoredClipsets->size() == 0)
		return TRect(0, 0, 0, 0);

	return *(*iStoredClipsets)[0];
#endif
}

inline CFont* CGraphicsProxy::Font() const
{
	return iFont;
}

#ifdef WINCE
inline void CGraphicsProxy::ConfirmSelectPen()
{
	if (iInnerGc != NULL)
	{
		if (m_PenStyle == CGraphicsContext::ENullPen)
		{
			HGDIOBJ hNullPen = ::GetStockObject(NULL_PEN);
			::SelectObject(iInnerGc, hNullPen);
		}
		else
		{
			if (m_hOldPen != NULL)
			{
				HPEN hOldPen = (HPEN)::SelectObject(iInnerGc, m_hOldPen);
				m_hOldPen = NULL;
			}

			if (m_hCurrPen != NULL)
			{
				::DeleteObject(m_hCurrPen);
				m_hCurrPen = NULL;
			}

			m_hCurrPen = ::CreatePen(PS_SOLID, 1, m_dwPenColor);
			m_hOldPen = (HPEN)::SelectObject(iInnerGc, m_hCurrPen);
		}
	}
}

inline void CGraphicsProxy::ConfirmSelectBrush()
{
	if (iInnerGc != NULL)
	{
		if (m_BrushStyle == CGraphicsContext::ENullBrush)
		{
			HBRUSH hNullBrush = (HBRUSH)::GetStockObject(NULL_BRUSH);
			::SelectObject(iInnerGc, hNullBrush);
		}
		else
		{
			if (m_hOldBrush != NULL)
			{
				::SelectObject(iInnerGc, m_hOldBrush);
				m_hOldBrush = NULL;
			}

			if (m_hCurrBrush != NULL)
			{
				::DeleteObject(m_hCurrBrush);
				m_hCurrBrush = NULL;
			}

			m_hCurrBrush = ::CreateSolidBrush(m_dwBrushColor);
			if (m_hCurrBrush == NULL)
			{
				int nErr = ::GetLastError();

				wchar_t sz[500] = {0};
				wsprintf(sz, L"CreateSolidBrush error: %d", nErr);
				::OutputDebugString(sz);
			}

			m_hOldBrush = (HBRUSH)::SelectObject(iInnerGc, m_hCurrBrush);
		}
	}
}
#endif

#endif 
